package com.lanhai.fnstore.dispatch;
  
import org.apache.log4j.Logger;
 
import com.lanhai.fnstore.Tools;
import com.lanhai.fnstore.cache.AgvCache;
import com.lanhai.fnstore.cache.EventLockCache;
import com.lanhai.fnstore.cache.MountCache;
import com.lanhai.fnstore.cache.AgvItem;
import com.lanhai.fnstore.cache.AgvNextTransEnum;
import com.lanhai.fnstore.cache.ParkCache;
import com.lanhai.fnstore.cache.ParkItem; 
import com.lanhai.fnstore.cache.ParkTypeEnum; 
import com.lanhai.fnstore.cache.RoadCache;
import com.lanhai.fnstore.cache.StationGroupCache;
import com.lanhai.fnstore.cache.TranfficAreaCache;
import com.lanhai.fnstore.log.LogData;
import com.lanhai.fnstore.log.LogQueue;
import com.lanhai.fnstore.push.MsgData;
import com.lanhai.fnstore.push.MsgQueue;
import com.lanhai.fnstore.queue.AgvEventUnit;
import com.lanhai.fnstore.queue.DispatchQueue;
import com.lanhai.fnstore.queue.DispatchUnit;
import com.lanhai.fnstore.queue.TargetCmdEnum;
import com.lanhai.fnstore.savestate.StateData;
import com.lanhai.fnstore.savestate.StateQueue;

/**
 * agv上报消息分析处理
 * 
 * @author virus408
 *
 */
public class AgvResolve {
	private static final Logger logger = Logger.getLogger(AgvResolve.class);
	
	/**
	 * 解析消息
	 * 
	 * @param unit
	 */
	public static void Resolve(AgvEventUnit unit) {   
		AgvData data = getData(unit.getData());
		if (data == null) {
			// 数据解析失败
			String errmsg=String.format("小车数据解析失败【%s】,IP【%s】", Tools.byte2HexStr(unit.getData()),unit.getIp());
			logger.error(errmsg); 
			Tools.writeErrLog(errmsg);
			return;
		}
		String datastr=String.format("IP:【%s】,收到数据:【%s】",unit.getIp(),Tools.byte2HexStr(unit.getData()));
		logger.info(datastr); 

		AgvItem agv = AgvCache.getAgvByID(data.getAgvId());
		if (agv == null) {
			// 对应的小车不存在
			String errmsg=String.format("没有找到IP为【%s】的小车",unit.getIp());
			logger.error(errmsg); 
			Tools.writeErrLog(errmsg);
			return;
		}
		switch (data.getType()) {
		case BZ:
			//// 避障 nothing
			StateQueue.Push(new StateData(0,agv.getAgvId()+"","避障中")); 
			break;
		case CD:
			//// 充电 nothing
			StateQueue.Push(new StateData(0,agv.getAgvId()+"","充电中")); 
			break;
		case DD:
			//// 到达 
			Log(data );
			// 更新小车位置
			AgvCache.updateAgvInfo(data.getAgvId(), data.getX(), data.getY(), data.getRoad(), true);
		 
			// 判断是否到主路
			if (data.getRoad() == 0) {
				//// 回到主路成功,不需要知道当前在哪个点
				// 走装卸工位的目标路线
				ParkItem tmp = ParkCache.getParkByNo(agv.getParkNo());
				if (tmp != null) {
					// 更新小车目标设备 
					AgvCache.updateNextCmd( agv.getAgvId(),tmp.getCmdSign());
					AgvToRoad(agv, tmp.getTargetRoad()); 
					logger.info(String.format("AGV回到主路，执行线路【%d】", tmp.getTargetRoad() ));
				} else {
					String errmsg=String.format("点位【%d】不见了？",agv.getParkNo());
					logger.error(errmsg);
				}
				return; // 结束操作，不考虑后面的到达停泊位
			}

			//// 到达停泊位
			int lv=RoadCache.getLv(agv.getRoadid()); 
			ParkItem park = ParkCache.getParkByPos(data.getX(), data.getY(),lv);
			if (park == null) { 
				// 到达了一个表中不存在的停泊位，这是一个数据BUG
				String errmsg=String.format("缺少坐标为【%d,%d】的点位",data.getX(),data.getY());
				logger.error(errmsg); 
				Tools.writeErrLog(errmsg); 
				 
				StateQueue.Push(new StateData(0,agv.getAgvId()+"","到达错误点位")); 
				return;
			}
			// 更新小车当前的所在的停泊位
			if(park.getType()!=ParkTypeEnum.QrBefore) {
				//传输工位前二维码的区域，不做点更新
				//1。如果是外部进来的，此时小车会直接扫二维码，进入对接工位,记录该点没意义
				//2.如果是退回主路，也不需要记录该点
				AgvCache.arrivedPark(agv.getAgvId(), park.getParkNo()); 
			}
			
			//保存状态
			AgvCache.saveState(agv.getAgvId());
			
			logger.info(String.format("小车【%d】到达点位:【%d】", agv.getAgvId(),park.getParkNo())); 
			//// 开始处理停泊位的各种类型
			if (park.getType() == ParkTypeEnum.Power) {
				//// 充电点   
				ParkEvent.arrivedPower(agv, park);  
			} else if (park.getType() == ParkTypeEnum.Wait) {
				//// 待命点，do nothing

			} else if (park.getType() == ParkTypeEnum.Trans) {
				//// 可传输工位 
				ParkEvent.arrivedTrans(agv, park);
			} else if (park.getType() == ParkTypeEnum.Decide) {
				////分配点
				 
				StateQueue.Push(new StateData(0,agv.getAgvId()+"","分配机台")); 
				ParkEvent.arrivedDecide(agv, park,data);  
			} else if (park.getType() == ParkTypeEnum.TransOut) {
				//// 释放工位
				StateQueue.Push(new StateData(0,agv.getAgvId()+"","释放资源中")); 
				 ParkEvent.arrivedRelease(agv, park) ;
			 
			} else if (park.getType() == ParkTypeEnum.Estimate) {
				//// 进入了预判断点
				StateQueue.Push(new StateData(0,agv.getAgvId()+"","到达预判断点")); 
				ParkEvent.arrivedEstimate(agv, park) ;
			}else if(park.getType()==ParkTypeEnum.Route) {
				//路由点，直接行始小车
				if(park.getRouteRoad()>0) { 
					AgvToRoad(agv, park.getRouteRoad());
				}else {
					StateQueue.Push(new StateData(0,agv.getAgvId()+"","没有路线分配")); 
					logger.error(String.format("点位【%d】没有分配路由路线", park.getParkNo()));
				}
			 
			}else if(park.getType()==ParkTypeEnum.Normal) {
				///普通点位，啥也不干
			}
			break;
		case DM:
			// 待命 nothing 
			break;
		case JG:
			// 交管 
			if (EventLockCache.lock(data.getAgvId(), data.getCmdStr())) { 	
				//保存状态
				logger.info(String.format("小车【%d】处于交管中", agv.getAgvId() )); 
				StateQueue.Push(new StateData(0,agv.getAgvId()+"","交管中")); 
				AgvCache.saveState(agv.getAgvId());
			}
			break;
		case JT:
			// 急停
			if (EventLockCache.lock(data.getAgvId(), data.getCmdStr())) {
				Log(data);
				AgvCache.updateAgvInfo(data.getAgvId(), data.getX(), data.getY(), data.getRoad(), true);
				StateQueue.Push(new StateData(0,agv.getAgvId()+"","急停")); 
				//保存状态
				AgvCache.saveState(agv.getAgvId());
			}
			break;
		case KCS:
			// 可传输,发起调度
			logger.info(String.format("小车【%d】发起调度", agv.getAgvId() )); 
			StateQueue.Push(new StateData(0,agv.getAgvId()+"","传输中")); 
				Log(data );
			 
				// 申请调度
				//NoticeJudgement(data);
			 
				
				//测试跑圈
				boolean flag=AgvCache.test(data.getAgvId());
				if(flag) {
					AgvResolve.AgvToRoad(agv, 0);
				}
			break;
		case LX:
			// 离线
			if (EventLockCache.lock(data.getAgvId(), data.getCmdStr())) {
				Log(data );
				AgvCache.abnormal(data.getAgvId());
				StateQueue.Push(new StateData(0,agv.getAgvId()+"","离线")); 
				//保存状态
				AgvCache.saveState(agv.getAgvId());
			}
			break;
		case SCCW: 
			//上层传出完成 
			break;
		case SCRW:
			//上层传入完成 
			break;
		case SCXRW:
			//上出下入完成 
			break; 
		case SRXCW:
			//上入下出完成 
			break; 
		case XCCW:
			//下层传出完成
			logger.info(String.format("小车【%d】下层传出完成", agv.getAgvId() )); 
			StateQueue.Push(new StateData(0,agv.getAgvId()+"","下层传出完成")); 
			MountCache.setTransing(agv.getAgvId(), false);
			MountCache.updateMount(agv.getAgvId(),true,false);
			AgvCache.updateNextTrans(agv.getAgvId(), AgvNextTransEnum.None);
			//调度
			NoticeJudgement(data);
			break;
		case XCRW:
			//下层传入完成 
			logger.info(String.format("小车【%d】下层传入完成", agv.getAgvId() )); 
			StateQueue.Push(new StateData(0,agv.getAgvId()+"","下层传入完成")); 
			MountCache.setTransing(agv.getAgvId(), false);
			MountCache.updateMount(agv.getAgvId(),false,false);
			AgvCache.updateNextTrans(agv.getAgvId(), AgvNextTransEnum.None);
			//调度
			NoticeJudgement(data);
			break; 
		case ZXWC:
			// 装卸完成 nothing
			break;
		case TZ:
			// 停止
			// AgvCache.updateAgvInfo(unit.getIp(), data.getX(), data.getY(), true);
			if (EventLockCache.lock(data.getAgvId(), data.getCmdStr())) {
				logger.info(String.format("小车【%d】停止", agv.getAgvId() )); 
				Log(data);
				AgvCache.abnormal(data.getAgvId());
				StateQueue.Push(new StateData(0,agv.getAgvId()+"","停止中")); 
				//保存状态
				AgvCache.saveState(agv.getAgvId());
			}
			break;
		case ERR:
			// 异常了
			if (EventLockCache.lock(data.getAgvId(), data.getCmdStr())) {
				Log(data );
				AgvCache.abnormal(data.getAgvId());
				
				//保存状态
				AgvCache.saveState(agv.getAgvId());
			}
			break;
		case XS:
			// 行驶中
			AgvCache.updateAgvInfo(data.getAgvId(), data.getX(), data.getY(), data.getRoad(), false); 
			StateQueue.Push(new StateData(0,agv.getAgvId()+"","行驶中")); 
			int areaID = TranfficAreaCache.intoArea(data.getX(), data.getY());
			if (areaID > 0) {
				if (agv.getAreaId() == areaID) {
					//小车自身锁
					logger.info(String.format("小车【%d】进入交管区域【%d】,小车是区域所有者", agv.getAgvId(),areaID )); 
					return;
				} 
				if (TranfficAreaCache.isService(areaID)) {
					//// 进入了故障服务区
					logger.info(String.format("小车【%d】进入服务区【%d】，释放资源", agv.getAgvId(),areaID )); 
					AgvCache.clearSource(data.getAgvId());
					AgvCache.saveState(agv.getAgvId());
					return;
				} 
				// 进入交管区
				AgvCache.updateAreaId(data.getAgvId(), areaID); 
				if (TranfficAreaCache.lockArea(areaID, agv.getAgvId())) {
					// 锁住区域成功,更新当前区域id  继续前进  
					logger.info(String.format("小车【%d】锁定了交管区域【%d】,小车继续行驶", agv.getAgvId(),areaID )); 
				} else {
					// 需要停下来
					logger.info(String.format("区域【%d】被其它小车锁住了，小车【%d】必须停下来",areaID, agv.getAgvId() )); 
					TranfficAreaCache.addStopAgv(areaID, agv.getAgvId());  
					// 向小车发送停车的指令
					AgvStopOrStart(agv, true);
				}
			} else {
				// 离开交管区,下一步操作交给leaveArea中去判断
				if (agv.getAreaId() > 0) {
					logger.info(String.format("小车【%d】离开了交管区域【%d】 ", agv.getAgvId(),areaID )); 
					AgvCache.leaveArea( data.getAgvId()); 
				}
			}
			
			//保存状态
			AgvCache.saveState(agv.getAgvId());
			
			break;
		default:
			break;

		}
	}

	/**
	 * 小车的指令解析
	 * 
	 * @param buf
	 * @return
	 */
	static AgvData getData(byte[] buf) {
		if (buf.length < 13) {
			return null;
		}
		// 只处理来自小车的数据
		if (buf[0] != 0xA1) {
			return null;
		}
		AgvData obj = new AgvData();
		if (buf[9] == 1) {
			// 小车异常
			obj.setType(AgvReceiveEnum.ERR);
			return obj;
		}

		obj.setType(AgvReceiveEnum.getAgvReceiveEnumByCode(buf[2]));
		if (buf[11] == 0x00) {
			// 全空
			obj.setUpFull(false);
			obj.setDownFull(false);
		} else if (buf[11] == 0x01) {
			// 上空下满
			obj.setUpFull(false);
			obj.setDownFull(true);
		} else if (buf[11] == 0x10) {
			// 上满下空
			obj.setUpFull(true);
			obj.setDownFull(false);
		} else if (buf[11] == 0x11) {
			// 全满
			obj.setUpFull(true);
			obj.setDownFull(true);
		}
		obj.setPower(buf[10]);
		obj.setRoad(buf[3]);
		obj.setAgvId(buf[1]);
		byte[] gpX = { buf[5], buf[6], 0, 0 };
		byte[] gpY = { buf[7], buf[8], 0, 0 };
		obj.setX(Tools.toIntL(gpX));
		obj.setY(Tools.toIntL(gpY));
		obj.setCmdStr(Tools.byte2HexStr(buf));
		obj.setData(buf);
		return obj;
	}

	/**
	 * 生成日志并写进队列里
	 * 
	 * @param data
	 * @param ip
	 */
	static void Log(AgvData data ) {
		LogData logobj = LogData.fromAgv(data );
		LogQueue.Push(logobj);
	}

	public static void AgvToRoad(AgvItem agv, int road) {
		if (agv == null || road<0)
			return;
		MsgData msg = new MsgData(agv.getIP(), agv.getPort(), AgvCmd.runRoad(agv.getAgvId(), road),
				String.format("启动路线%s", road));
		msg.setAgvId(agv.getAgvId());
		MsgQueue.Push(0,msg);
	}

	public static void AgvStopOrStart(AgvItem agv, boolean stop) {
		if (agv == null)
			return;
		String rmark="交管停车";
		if(stop==false) {
			rmark="交管启动";
		}
		MsgData stopCmd = new MsgData(agv.getIP(), agv.getPort(), AgvCmd.stopOrstart(agv.getAgvId(), stop), rmark);
		stopCmd.setAgvId(agv.getAgvId());
		MsgQueue.Push(0,stopCmd);
	}
	
	
	
	/**
	 * 通知预判断位放行小车
	 * @param parkNo 当前判断工位，而不是预判断位
	 * @param agvIp
	 */
	static void  NoticeEstimate(int parkNo ) {
		int sourceDot = StationGroupCache.getReadySource(parkNo);
		if (sourceDot == 0) return; 
		DispatchUnit dus = new DispatchUnit(); 
		dus.setParkNo(sourceDot);
		dus.setTarget(TargetCmdEnum.NoticeEstimate);
		DispatchQueue.Push(dus);
	}
	
	static void NoticeJudgement(AgvData data) {
		int pkno=AgvCache.getParkNoById(data.getAgvId()); 
		Log(data );
		DispatchUnit du = new DispatchUnit();
		du.setParkNo(pkno);  
		du.setTarget(TargetCmdEnum.Judgement);
		DispatchQueue.Push(du); 
	}
}
